# -*- coding:utf-8 -*-
import configparser
import json
import re
import time
import yaml
from selenium.common import exceptions
from selenium.common.exceptions import NoSuchElementException
import os
from time import sleep
import allure
from selenium.webdriver.support import expected_conditions
from common.log.log import log
from config.conf import ALLURE_IMG_DIR, BASE_DIR, Test_data
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

from data.names import PublicSel

"""
截图功能模块
"""


@allure.step('picture 截图')
def picture():
    """
    截图功能
    :return:
    """
    img_dir = ALLURE_IMG_DIR
    img_file = ALLURE_IMG_DIR + f'\\tmp.jpg'
    if not os.path.isdir(img_dir):
        os.makedirs(img_dir)
        log.info(f'创建目录：{img_dir}')
    else:
        [os.remove(os.path.join(img_dir, file_name)) for file_name in os.listdir(img_dir)]
        log.info(f'清空目录:{img_dir}')
    sleep(1)
    # im = pyautogui.screenshot()
    # im.save(img_file)
    return img_file


@allure.step('添加截图步骤')
def allure_picture():
    """
    allure添加截图附件
    :return:
    """
    with open(picture(), "rb") as f:
        log.info('用例正常截图')
        allure.attach(f.read(), name=f"屏幕截图", attachment_type=allure.attachment_type.JPG)


def allure_save_screenshot(drive, name):
    """
    allure添加截图附件（使用Chrome自带截图）
    :return:
    """
    with open(chrom_save_screenshot(drive), "rb") as f:
        log.info('用例截图')
        allure.attach(f.read(), name=name, attachment_type=allure.attachment_type.JPG)


@allure.step('滑动查找元素')
def scroll_find_element(driver, sele):
    """
    滑动查找元素 找到就退出,
    没找到就再从头开始找一次
    :param driver:
    :param by:
    :param ele:
    :return:
    """
    scroll = 0
    loop = 0
    while loop <= 1:
        try:
            # sele = (by, ele)
            object_left_click(driver, sele)
            log.info(f'滑动查找元素后，点击元素:{sele}')
        except NoSuchElementException:
            js = "document.documentElement.scrollTop={0}".format(scroll)
            driver.execute_script(js)
            scroll += 300
            log.error(f'没找到元素，往下滑动{scroll}')
        else:
            log.info("找到元素")
            break
        if scroll > 6000:
            loop += 1
            scroll = 0
            log.error("没找到元素 第%s次" % loop)


@allure.step('初始化，回主页')
def ini_home(drive):
    """
    初始化, 回主页
    :param drive:
    :return:
    """
    drive.find_element(By.LINK_TEXT, 'TesterHome').click()
    sleep(2)
    log.info('初始化, 回主页')


@allure.step('鼠标左键单击')
# def object_left_click(drive, by, sel, timeout=10):
def object_left_click(drive, sel, timeout=20):
    """
    鼠标左键单击（元素中是否可见并且是enable的，代表可点击）
    https://www.cnblogs.com/an5456/p/11279879.html  这篇内容写的较详细
    """
    try:
        # WebDriverWait(drive, timeout).until(expected_conditions.element_to_be_clickable((by, sel))).click()
        WebDriverWait(drive, timeout).until(expected_conditions.element_to_be_clickable((sel))).click()
        sleep(0.5)
        selen = re.sub('[^\u4e00-\u9fa5]+', '', str(sel))
        if len(selen) > 0:
            log.info(f"点击：{selen}")
        return True
    except Exception as e:
        log.error(f"鼠标左键单击操作异常，异常为：{e}")
        raise e


@allure.step('元素可点击')
def elements_clickable(drive, by, sel, timeout=30):  # elements_visible
    try:
        # https://www.cnblogs.com/an5456/p/11279879.html 参考这篇文章
        WebDriverWait(drive, timeout).until(expected_conditions.element_to_be_clickable((by, sel)))
        return True
    except Exception as e:
        log.error(f"已达到超时时间元素【{sel}】仍未加载出，不可点击，异常为：{e}")
        raise e


@allure.step('元素可见')
def elements_visibility(drive, by, sel, timeout=30):
    try:
        # https://www.cnblogs.com/an5456/p/11279879.html 参考这篇文章
        WebDriverWait(drive, timeout).until(expected_conditions.visibility_of_element_located((by, sel)))
        return True
    except Exception as e:
        log.error(f"已达到超时时间元素仍然不可见，异常为：{e}")
        raise e


@allure.step('输入内容')
def object_send_keys(drive, sel, value, timeout=10):
    """
    输入内容
    """
    try:
        WebDriverWait(drive, timeout).until(expected_conditions.element_to_be_clickable((sel))).clear()
        sleep(0.5)
        WebDriverWait(drive, timeout).until(expected_conditions.element_to_be_clickable((sel))).send_keys(value)
        sleep(0.5)
        selen = re.sub('[^\u4e00-\u9fa5]+', '', str(sel))
        if len(selen) > 0:
            log.info(f"点击：{selen}，并输入值：{value}")
        return True
    except Exception as e:
        log.error(f"输入内容操作异常，异常为：{e}")
        raise e


@allure.step('先清空内容，再输入内容')
def object_clear_send_keys(drive, sel, value, timeout=10):
    """
    先清空内容，再输入内容
    """
    try:
        sele = WebDriverWait(drive, timeout).until(expected_conditions.element_to_be_clickable((sel)))
        sele.clear()
        sleep(1)
        sele.send_keys(value)
        return True
    except Exception as e:
        log.error(f"输入内容操作异常，异常为：{e}")
        raise e


@allure.step('F12是否出现异常')
def check_f12_log(browser_log):
    """
    检查页面F12是否出现验证级别、错误级别日志，有返回False
    :param log:
    :return:
    """
    result = []
    for i in range(len(browser_log)):
        result.append(browser_log[i]['level'])
    if 'SEVERE' in result:
        log.error(browser_log)
        log.error(f'f12出现异常，请检查，级别为：{result}')
        return False
    elif 'ERROR' in result:
        log.error(browser_log)
        log.error(f'f12出现异常，请检查，级别为：{result}')
        return False
    else:
        log.info(f'页面f12暂未出现明显报错！browser_Log为：{result}')
        return True


@allure.step('chrom自带截图')
def chrom_save_screenshot(drive):
    """
    chrom自带截图
    """
    try:
        img_dir = ALLURE_IMG_DIR
        img_file = ALLURE_IMG_DIR + f'\\tmp_chrom_save_screenshot.jpg'
        if not os.path.isdir(img_dir):
            os.makedirs(img_dir)
            log.info(f'创建目录：{img_dir}')
        else:
            [os.remove(os.path.join(img_dir, file_name)) for file_name in os.listdir(img_dir)]
            log.info(f'清空目录:{img_dir}')
        sleep(1)
        drive.save_screenshot(img_file)
        return img_file
    except Exception as e:
        log.error(f'截图发生异常：{e}')
        raise


@allure.step('读取指定的yaml文件并返回数据流')
def read_yaml(yaml_path):
    """
    读取指定的yaml文件并返回数据流
    """
    with open(yaml_path, 'r', encoding='utf-8') as f:
        yaml_data = yaml.load(f, yaml.FullLoader)
    return yaml_data


@allure.step('检查页面无报错')
def check_page_tips(drive):
    """
    检查页面无报错，或操作后弹出成功tips
    获取页面元素内容（一般为操作后的提示，如：操作成功 / 错误报错等）
    包含“成功”或“无提示”，返回True
    包含“失败或error”，返回False
    """
    start_time = time.time()
    for i in range(2):
        try:
            drive.implicitly_wait(3)
            sel_text = drive.find_element(By.XPATH, "//div[@role='alert']").text
            if '成功' in sel_text:
                log.info(sel_text)
                return True
            elif 'error' or 'Error' or '错误' or '失败' or '异常' or '500' or 'failed' in sel_text:
                log.error(f'页面弹出tips为：{sel_text}')
                return False
        except NoSuchElementException:
            log.info(f'循环查找第{i + 1}次，暂时无提示tips')
    end_time = time.time()
    use_time = end_time - start_time
    log.info(f'查找了{round(use_time)}秒，未发现错误哟 ^.^# ')
    return True


@allure.step('获取tips文案')
def get_tips_message(drive):
    """获取tips文案"""
    try:
        for i in range(5):
            message = get_ele_text(drive, PublicSel.el_tips())
            if message != '':
                break
            log.info(f'循环了{i + 1}次找tips')
        # allure_save_screenshot(drive, '获取tips的界面截图')  # 不要截图了浪费资源
        log.info(f'获取tips提示文案为：{message}')
        return message
    except Exception as e:
        log.error(f'异常为：{e}')


@allure.step('获取指定元素的text值')
def get_ele_text(driver, els, timeout=10, mode=0):
    """获取指定元素的text值"""
    try:
        # 等待元素在页面上出现
        element = WebDriverWait(driver, timeout).until(
            EC.presence_of_element_located((els))
        )
        if mode == 0:
            log.info(element.text)
            return element.text
        elif mode == 1:
            log.info(element.get_attribute('textContent'))
            return element.get_attribute('textContent')
    except Exception as e:
        print(f"错误：{e}")
        return None


@allure.step('删除二次确认动作')
def action_delete(drive):
    """ui自动化，删除二次确认动作"""
    try:
        del_ele = drive.find_elements(By.XPATH, "//span[contains(text(),'删除')]")
        del_ele[-1].click()  # 点击删除
        object_left_click(drive, PublicSel.el_ok_button())  # 弹框点击确定
    except Exception as e:
        log.error(f'异常为;：{e}')


@allure.step('xpath断言元素存在')
def iselement(drive, elem, mode=0):
    """
    xpath断言元素存在
    """
    try:
        sleep(1)
        if mode == 0:
            # 该模式兼容Excel驱动的页面菜单元素断言
            drive.find_element(By.XPATH, elem)
            return True
        elif mode == 1:
            # 断言页面元素文案，span
            drive.find_element(By.XPATH, f"//span[contains(text(),{elem})]")
            return True
        elif mode == 2:
            # 断言页面元素文案，div
            drive.find_element(By.XPATH, f"//div[contains(text(),{elem})]")
            return True
    except exceptions.NoSuchElementException:
        log.error(f'xpath断言元素存在出现异常：{elem}')
        return False


@allure.step('json写入到yaml')
def json_to_yaml(json_data, yaml_file_path):
    try:
        # 写入YAML文件
        with open(yaml_file_path, 'w') as yaml_file:
            yaml.dump(json_data, yaml_file, default_flow_style=False, encoding='utf-8', allow_unicode=True)
        log.info("JSON内容已成功写入YAML文件。")
    except Exception as e:
        log.error("json写入到yaml发生错误：", e)


def case_name(name):
    allure.attach(body=name, name="用例ID", attachment_type=allure.attachment_type.JSON)


@allure.step('比对字符串')
def match_string(str1, str2):
    """
    比对字符串
    两者完全匹配返回True反之False
    新增日期：2023-8-4 10:37:31
    """
    try:
        # 先用 in 进行比对
        if str1 in str2:
            return True
        # 找到较短的字符串长度
        min_length = min(len(str1), len(str2))
        # 使用循环比较字符，并构建不一致的字符串列表
        diff_list = []  # 存放不同的字符串
        key = []  # 记录出现不同字符串的索引值
        for i in range(min_length):
            if str1[i] != str2[i]:
                diff_list.append(str1[i])
                key.append(i)
        if len(diff_list) == 0:
            return True
        else:
            log.error(f"对比结果符合预期")
            log.error(f'期望值为：{str1[key[0]:]}')
            log.error(f'实际值为：{str2[key[0]:]}')
            return False
    except Exception as e:
        log.error(f'异常为：{e}')


@allure.step('获取环境域名')
def get_domain(env='test'):
    """
    获取环境域名
    """
    # 环境地址
    env_url = read_ini()['host']['url']

    if env == 'test':
        return env_url.split('.com')[0] + '.com'
    if env == 'on_line':
        return env_url.split('.com')[0] + '.com'


@allure.step('读取ini文件中节点')
def read_ini(file):
    """读取ini文件中节点/选项/选项值"""
    config = configparser.ConfigParser()
    config.read(file, encoding='utf-8')
    # 读取ini文件所有节点
    # results = config.sections()
    return config
    # print(f'所有节点为：{results}')
    # # 读取所有的选线
    # for result in results:
    #     options = config.options(result)  # 获取节点下所有选项
    #     print(f'{result}所有选项为：{options}')
    #     for option in options:
    #         value = config.get(section=result, option=option)  # 获取选项对应选项值
    #         print(f'{option}所有选项值为：{value}')


@allure.step('编辑ini文件')
def edit_ini():
    """
    编辑ini文件：增加节点、增加选项、修改选项值、删除节点等
    """
    config = configparser.ConfigParser()
    with open('config_yg.ini', 'a+') as f1:
        # 1、添加节点
        config.add_section('offer2')
        # 2、添加选项及选项值
        config.set(section='offer2', option='offer01', value='10k')
        config.set(section='offer2', option='offer02', value='20k')
        # 3、移除选项
        config.remove_option(section='offer2', option='offer01')
        # 4、移除节点
        config.remove_section(section='offer2')

        # 保存文件
        config.write(f1)


@allure.step('获取元素属性')
def get_elelment(driver, locator, timeout=15, poll_frequency=0.5, desc=None, elements=False):
    """

    Args:
        locator:  元素的定位器
            eg. 'id','ls_username'
        timeout: 显式等待超时时间
        poll_frequency: 轮训频率
        elements： 是否定位多个元素-->此时的返回是个 list
    Returns: 元素

    """
    # PO1:The public methods represent the services that the page offers
    try:
        if not elements:
            return WebDriverWait(driver, timeout, poll_frequency).until(EC.visibility_of_element_located(locator))
        else:
            return WebDriverWait(driver, timeout, poll_frequency).until(EC.visibility_of_all_elements_located(locator))
    except:
        from time import strftime  # arrow
        import os
        curtime = strftime('%Y%m%d%H%M%S')
        # {os.sep} 能适配操作系统
        """/Users/liaogan/huawang-api/data"""
        driver.save_screenshot(f'{Test_data}{os.sep}{desc}.png')
        # driver.save_screenshot(f'{PollyPath.screenshots_path}{os.sep}{desc}.png')
        log.error(f'{desc}元素找不到')
        return False  # 可以借用这个定位同时判断元素是否存在

        # 没有遵循PO6


@allure.step('点击元素--->>>')
def click_element(drive, locator, desc=None):
    """
    点击指定的元素
    Args:
        locator: 待点击元素的定位器

    Returns: None

    """
    get_elelment(drive, locator, desc=desc).click()


@allure.step('获取指定元素文本--->>>')
def get_element_text(drive, locator, desc=None):
    # print(locator)
    """
    获取指定元素的文本
    Args:
        locator: 指定元素的定位器

    Returns: 指定的元素的文本

    """
    return get_elelment(drive, locator, desc=desc).text


@allure.step('获取指定所有元素文本--->>>')
def get_elements_text(drive, locator, desc=None):
    allelements = get_elelment(drive, locator, elements=True, desc=desc)  # list
    return [element.text for element in allelements]


@allure.step('输入内容--->>>')
def input_text(drive, locator, text, append=False, desc=None):
    """
    Args:
        locator: 待输入文本的元素的定位器
        text:  待输入的文本
        append: 是否要追加输入，默认不追加(False)

    Returns: None
    """
    if append:
        get_elelment(drive, locator, desc=desc).send_keys(text)
    else:
        get_elelment(drive, locator, desc=desc).clear()
        get_elelment(drive, locator, desc=desc).send_keys(text)


def Move_scroll_bar(driver, x, y):
    """
    移动滚动条到什么位置，几分之一
    """
    # 计算屏幕的宽度和高度
    screen_width = driver.execute_script("return window.screen.width")
    screen_height = driver.execute_script("return window.screen.height")
    # 计算屏幕的 2/3 位置（向下滚动）
    scroll_pos = int(screen_height * x / y)
    # 执行滚动操作
    driver.execute_script("window.scrollTo({}, {});".format(0, scroll_pos))


def Move_scroll_bar_top_low(driver, top=True):
    """
    top：移动滚动条到顶部
    top：false 移动滚动条到底部
    """

    if top:
        driver.execute_script("window.scrollTo(0, 0);")
    else:
        # 执行滚动操作
        driver.execute_script("window.scrollTo(0,2500 );")


def show_time(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        end_time = time.time()
        log.info('花王云客api接口用例执行用例时间为{}'.format(end_time - start_time))

    return inner


def show_time_UI(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        end_time = time.time()
        log.info('花王云客UI用例执行时间为{}'.format(end_time - start_time))

    return inner


if __name__ == '__main__':
    print(edit_ini())
